﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Xml;
using SHomeWorkshop.LunarConcept.Adorners;
using SHomeWorkshop.LunarConcept.Controls;
using SHomeWorkshop.LunarConcept.Enums;
using SHomeWorkshop.LunarConcept.Tools;
using SHomeWorkshop.LunarConcept.Widgets.Interfaces;
using SHomeWorkshop.LunarMind;

namespace SHomeWorkshop.LunarConcept.Widgets
{
    /// <summary>
    /// 创建时间：2012年1月16日前
    /// 创建者：  杨震宇
    /// 
    /// 主要用途：直线部件。
    /// </summary>
    public class StraitLineWidget : ArrowLineWidget, ICanSameSize, Interfaces.ILinkableLine, Interfaces.ITextRotate
    {
        #region 构造方法=====================================================================================================

        /// <summary>
        /// [构造方法]
        /// </summary>
        public StraitLineWidget(PageEditorReader masterEditor)
            : base(masterEditor)
        {
            widgetType = Enums.WidgetTypes.StraitLine;
            widgetClassLocalName = Widget.GetWidgetClassLocalName(this.GetType().Name);

            mainLine.Stroke = mainLine.Fill = this.widgetForeColor;
            mainLine.StrokeThickness = widgetLineWidth;

            this.mainCanvas.Children.Add(mainLine);

            Canvas.SetZIndex(mainLine, 0);

            #region 两个箭头。
            //startArrowPath.Cursor = Cursors.Arrow;
            startArrowPath.Fill = startArrowPath.Stroke = this.widgetForeColor;
            startArrowPath.StrokeThickness = widgetLineWidth;
            PathGeometry startArrowPg = new PathGeometry();
            startArrowPath.Data = startArrowPg;

            startArrowPathFigure.IsClosed = true;
            startArrowPathFigure.IsFilled = true;
            startArrowPathFigure.Segments.Add(startArrowPolyLineSegment);

            startArrowPg.Figures.Add(startArrowPathFigure);

            //endArrowPath.Cursor = Cursors.Arrow;
            endArrowPath.Fill = endArrowPath.Stroke = this.widgetForeColor;
            endArrowPath.StrokeThickness = widgetLineWidth;
            PathGeometry endArrowPg = new PathGeometry();
            endArrowPath.Data = endArrowPg;

            endArrowPathFigure.IsClosed = true;
            endArrowPathFigure.IsFilled = true;
            endArrowPathFigure.Segments.Add(endArrowPolyLineSegment);

            endArrowPg.Figures.Add(endArrowPathFigure);

            mainCanvas.Children.Add(startArrowPath);
            mainCanvas.Children.Add(endArrowPath);

            Canvas.SetZIndex(startArrowPath, 1);
            Canvas.SetZIndex(endArrowPath, 1);
            #endregion

            this.commentAdorner = new CommentAdorner(this.mainLine, this) { Visibility = System.Windows.Visibility.Collapsed };//默认不显示。

            this.hyperLinkAdorner = new HyperLinkAdorner(this.mainLine, this) { Visibility = Visibility.Collapsed };
            this.hyperLinkAdorner.MouseLeftButtonUp += new MouseButtonEventHandler(hyperLinkAdorner_MouseLeftButtonUp);

            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.mainCanvas);
            if (adornerLayer == null)
            {
                MessageBox.Show("　　未找到Widget的装饰层！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            else
            {
                adornerLayer.Add(this.commentAdorner);//这个要在各具体部件类中添加。
                adornerLayer.Add(this.hyperLinkAdorner);//这个要在各具体部件类中添加。
            }

            //最后添加文本面板。//已移动到基类中。
            //this.mainCanvas.Children.Add(this.mainTextPanel);
            //Canvas.SetZIndex(this.mainTextPanel, 2);
        }

        #endregion


        #region 字段与属性===================================================================================================

        private double textRotateAngle = 0;
        /// <summary>
        /// [读写]文本旋转角度。取值范围：[-180,180]。
        /// </summary>
        [Tools.LunarProperty("TextRotateAngle", PropertyDateType.Double)]
        public double TextRotateAngle
        {
            get { return textRotateAngle; }
            set
            {
                if (value > 180)
                {
                    textRotateAngle = 180;
                }
                else if (value < -180)
                {
                    textRotateAngle = -180;
                }
                else
                {
                    textRotateAngle = value;
                }

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.TextRotateAngleTag, textRotateAngle.ToString());
                }

                this.RefreshTextRotateAngle();
            }
        }

        /// <summary>
        /// [只读]返回以直线为对角线的矩形的左上角。
        /// </summary>
        public override Point TopLeft
        {
            get
            {
                double minLeft, minTop, maxRight, maxBottom;

                minLeft = Math.Min(startPoint.X, endPoint.X);
                maxRight = Math.Max(startPoint.X, endPoint.X);

                minTop = Math.Min(startPoint.Y, endPoint.Y);
                maxBottom = Math.Min(startPoint.Y, endPoint.Y);

                return new Point(minLeft, minTop);
            }
        }

        /// <summary>
        /// [只读]返回以直线为对角线的矩形的右下角。
        /// </summary>
        public override Point BottomRight
        {
            get
            {
                double minLeft, minTop, maxRight, maxBottom;

                minLeft = Math.Min(startPoint.X, endPoint.X);
                maxRight = Math.Max(startPoint.X, endPoint.X);

                minTop = Math.Min(startPoint.Y, endPoint.Y);
                maxBottom = Math.Max(startPoint.Y, endPoint.Y);

                return new Point(maxRight, maxBottom);
            }
        }

        #region 两侧箭头

        public override void RefreshArrows()
        {
            base.RefreshArrows();

            switch (arrows)
            {
                case ArrowType.None:
                    {
                        mainLine.X1 = startPoint.X; mainLine.Y1 = startPoint.Y;
                        mainLine.X2 = endPoint.X; mainLine.Y2 = endPoint.Y;
                        return;
                    }
                case ArrowType.Start:
                    {
                        startArrowPathFigure.StartPoint = startPoint;
                        ArrowPoints apStart = new ArrowPoints(
                            startArrowPathFigure.StartPoint, endPoint, widgetLineWidth * 2);

                        startArrowPolyLineSegment.Points = new PointCollection(){
                            apStart.ArrowLeftPoint,apStart.ArrowRightPoint,
                        };
                        mainLine.X1 = apStart.ArrowRealTopPoint.X; mainLine.Y1 = apStart.ArrowRealTopPoint.Y;

                        mainLine.X2 = endPoint.X; mainLine.Y2 = endPoint.Y;
                        return;
                    }
                case ArrowType.End:
                    {
                        endArrowPathFigure.StartPoint = endPoint;
                        ArrowPoints apEnd = new ArrowPoints(
                            endPoint, startPoint, widgetLineWidth * 2);

                        endArrowPolyLineSegment.Points = new PointCollection(){
                            apEnd.ArrowLeftPoint,apEnd.ArrowRightPoint,
                        };
                        mainLine.X2 = apEnd.ArrowRealTopPoint.X; mainLine.Y2 = apEnd.ArrowRealTopPoint.Y;

                        mainLine.X1 = startPoint.X; mainLine.Y1 = startPoint.Y;
                        return;
                    }
                default://All
                    {
                        startArrowPathFigure.StartPoint = startPoint;
                        ArrowPoints apStart = new ArrowPoints(
                            startArrowPathFigure.StartPoint, endPoint, widgetLineWidth * 2);

                        startArrowPolyLineSegment.Points = new PointCollection(){
                            apStart.ArrowLeftPoint,apStart.ArrowRightPoint,
                        };
                        mainLine.X1 = apStart.ArrowRealTopPoint.X; mainLine.Y1 = apStart.ArrowRealTopPoint.Y;

                        endArrowPathFigure.StartPoint = endPoint;
                        ArrowPoints apEnd = new ArrowPoints(
                            endPoint, startPoint, widgetLineWidth * 2);

                        endArrowPolyLineSegment.Points = new PointCollection(){
                            apEnd.ArrowLeftPoint,apEnd.ArrowRightPoint,
                        };
                        mainLine.X2 = apEnd.ArrowRealTopPoint.X; mainLine.Y2 = apEnd.ArrowRealTopPoint.Y;
                        return;
                    }
            }
        }

        /// <summary>
        /// 刷新箭头状态。
        /// </summary>
        /// <param name="drawLine">若为真，按已设置的startPoint/startCPpoint/endCPPoint/endPoint画线及箭头。</param>
        private void RefreshArrows(bool drawLine)
        {
            base.RefreshArrows();

            if (drawLine) DrawLine();
        }

        #endregion
        
        private string endMasterId = string.Empty;
        /// <summary>
        /// [读写]尾端点挂接到的部件的Id。
        /// </summary>
        [Tools.LunarProperty("EndMasterId", PropertyDateType.String)]
        public string EndMasterId
        {
            get { return endMasterId; }
            set
            {
                endMasterId = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.EndMasterIdTag, value);
                }
            }
        }

        private Point endPoint = new Point();
        /// <summary>
        /// [读写]线的终点坐标。
        /// </summary>
        [Tools.LunarProperty("EndPoint", PropertyDateType.Point)]
        public Point EndPoint
        {
            get { return endPoint; }
            set
            {
                endPoint = FormatPoint(value);

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.EndPointTag, endPoint.ToString());
                }

                RefreshLocation();
            }
        }

        private LinkToPoint endLinkTo = LinkToPoint.Center;
        /// <summary>
        /// 作连接线时才有意义。
        /// [读写]线的尾端点连接到目标部件的哪个连接点。
        /// </summary>
        [Tools.LunarProperty("EndLinkTo", PropertyDateType.LinkToPoint)]
        public LinkToPoint EndLinkTo
        {
            get { return endLinkTo; }
            set
            {
                endLinkTo = value;

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.EndLinkToTag, endLinkTo.ToString());
                }

                DrawLine();
            }
        }

        /// <summary>
        /// [只读]是否被挂接到两个ContentWidget。
        /// </summary>
        public bool IsLinked
        {
            get
            {
                if (startMasterId == string.Empty && endMasterId == string.Empty)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }

        /// <summary>
        /// 连接线永不被设置为锁定。
        /// </summary>
        [Tools.LunarProperty("IsLocked", PropertyDateType.Bool)]
        public override bool IsLocked
        {
            get
            {
                if (IsLinked) return false;

                return base.IsLocked;
            }
            set
            {
                if (IsLinked) return;//连接线不被设置为锁定。

                base.IsLocked = value;
            }
        }

        private Line mainLine = new Line() { Cursor = Cursors.Hand };

        private string startMasterId = string.Empty;
        /// <summary>
        /// [读写]首端点挂接的部件的Id。
        /// </summary>
        [Tools.LunarProperty("StartMasterId", PropertyDateType.String)]
        public string StartMasterId
        {
            get { return startMasterId; }
            set
            {
                startMasterId = value;
                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.StartMasterIdTag, value);
                }
            }
        }

        private Point startPoint = new Point(0, 0);
        /// <summary>
        /// [读写]线的起点坐标。
        /// </summary>
        [Tools.LunarProperty("StartPoint", PropertyDateType.Point)]
        public Point StartPoint
        {
            get { return startPoint; }
            set
            {
                startPoint = FormatPoint(value);

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.StartPointTag, startPoint.ToString());
                }

                RefreshLocation();
            }
        }

        private LinkToPoint startLinkTo = LinkToPoint.Center;
        /// <summary>
        /// 作连接线时才有意义。
        /// [读写]线的首端点连接到目标部件的哪个连接点。
        /// </summary>
        [Tools.LunarProperty("StartLinkTo", PropertyDateType.LinkToPoint)]
        public LinkToPoint StartLinkTo
        {
            get { return startLinkTo; }
            set
            {
                startLinkTo = value;

                if (this.xmlData != null)
                {
                    this.xmlData.SetAttribute(XmlTags.StartLinkToTag, startLinkTo.ToString());
                }

                DrawLine();
            }
        }

        #endregion


        #region 方法=========================================================================================================

        public override void Build()
        {
            base.Build();

            if (this.xmlData == null) return;

            XmlAttribute attrStartMasterId = this.xmlData.GetAttribute(XmlTags.StartMasterIdTag);
            if (attrStartMasterId != null)
            {
                this.startMasterId = attrStartMasterId.Value;
            }

            XmlAttribute attrEndMasterId = this.xmlData.GetAttribute(XmlTags.EndMasterIdTag);
            if (attrEndMasterId != null)
            {
                this.endMasterId = attrEndMasterId.Value;
            }

            XmlAttribute attrStartPoint = this.xmlData.GetAttribute(XmlTags.StartPointTag);
            if (attrStartPoint != null)
            {
                this.startPoint = FormatPoint(Point.Parse(attrStartPoint.Value));
            }

            XmlAttribute attrStartLinkTo = this.xmlData.GetAttribute(XmlTags.StartLinkToTag);
            if (attrStartLinkTo != null)
            {
                this.startLinkTo = (LinkToPoint)Enum.Parse(typeof(LinkToPoint), attrStartLinkTo.Value);
            }

            XmlAttribute attrEndPoint = this.xmlData.GetAttribute(XmlTags.EndPointTag);
            if (attrEndPoint != null)
            {
                this.endPoint = FormatPoint(Point.Parse(attrEndPoint.Value));
            }

            XmlAttribute attrEndLinkTo = this.xmlData.GetAttribute(XmlTags.EndLinkToTag);
            if (attrEndLinkTo != null)
            {
                this.endLinkTo = (LinkToPoint)Enum.Parse(typeof(LinkToPoint), attrEndLinkTo.Value);
            }

            //包含RefreshArrows()调用。
            this.RefreshLocation();

            //此类是下列Style properties的“最终实现类”。因此调用，
            this.RefreshWidgetLineColor();
            this.RefreshWidgetLineWidth();
            this.RefreshLineDash();

        }
        
        /// <summary>
        /// 根据“IsLinked”的结果分别重绘线条。
        /// </summary>
        public void DrawLine()
        {
            if (masterEditor == null) return;
            if (IsLinked)
            {
                Widget startWidget = masterEditor.GetWidget(this.StartMasterId);
                Widget endWidget = masterEditor.GetWidget(this.EndMasterId);
                if (startWidget == null || endWidget == null)
                {
                    DrawLine(this.startPoint, this.endPoint);
                    return;
                }

                Rect startRect = startWidget.OuterRect;
                Rect endRect = endWidget.OuterRect;
                DrawLine(startRect, endRect);
            }
            else
            {
                DrawLine(this.startPoint, this.endPoint);
            }
        }

        /// <summary>
        /// “IsLinked”为false时，表示此线未连接到ICanLinkedWidget部件。
        /// </summary>
        /// <param name="startPoint">首端点。</param>
        /// <param name="endPoint">尾端点。</param>
        private void DrawLine(Point startPoint, Point endPoint)
        {
            if (arrows == ArrowType.None)
            {
                mainLine.X1 = startPoint.X; mainLine.Y1 = startPoint.Y;
                mainLine.X2 = endPoint.X; mainLine.Y2 = endPoint.Y;
            }
            else
            {
                if (arrows == ArrowType.All || arrows == ArrowType.Start)
                {
                    startArrowPathFigure.StartPoint = startPoint;
                    Tools.ArrowPoints apStart = new Tools.ArrowPoints(
                        startArrowPathFigure.StartPoint, endPoint, widgetLineWidth * 2);

                    startArrowPolyLineSegment.Points = new PointCollection(){
                        apStart.ArrowLeftPoint,apStart.ArrowRightPoint,
                    };

                    mainLine.X1 = apStart.ArrowRealTopPoint.X; mainLine.Y1 = apStart.ArrowRealTopPoint.Y;
                }
                else
                {
                    mainLine.X1 = startPoint.X; mainLine.Y1 = startPoint.Y;
                }

                if (arrows == ArrowType.All || arrows == ArrowType.End)
                {
                    endArrowPathFigure.StartPoint = endPoint;
                    Tools.ArrowPoints apEnd = new Tools.ArrowPoints(
                        endArrowPathFigure.StartPoint, startPoint, widgetLineWidth * 2);

                    endArrowPolyLineSegment.Points = new PointCollection(){
                        apEnd.ArrowLeftPoint,apEnd.ArrowRightPoint,
                    };

                    mainLine.X2 = apEnd.ArrowRealTopPoint.X; mainLine.Y2 = apEnd.ArrowRealTopPoint.Y;
                }
                else
                {
                    mainLine.X2 = endPoint.X; mainLine.Y2 = endPoint.Y;
                }
            }
        }

        /// <summary>
        /// ★此方法不会调用DrawLine(Point,Point,Point,Point)方法来进行绘制。
        /// 
        /// “IsLinked”属性为true时，曲线被连接到两个ICanLinkedWidget部件。
        /// 此时，曲线的位置和形状由这两个部件的外框和曲线的两个控制点决定。
        /// 曲线的首端点、尾端点将自动计算，计算的规则是：“首控制点到首矩形的中心点与矩形的交战为首端点”。
        /// </summary>
        /// <param name="startRect">首连接部件外框。</param>
        /// <param name="endRect">尾连接部件外框。</param>
        private void DrawLine(Rect startRect, Rect endRect)
        {
            if (startRect.IntersectsWith(endRect))
            {
                mainLine.Visibility = Visibility.Hidden;
                return;
            }

            mainLine.Visibility = Visibility.Visible;
            this.RefreshArrows(false);

            //边缘留点空白区。
            startRect.X -= 4; startRect.Y -= 4; startRect.Width += 8; startRect.Height += 8;
            endRect.X -= 4; endRect.Y -= 4; endRect.Width += 8; endRect.Height += 8;

            if (this.startLinkTo == LinkToPoint.Center && this.endLinkTo == LinkToPoint.Center)
            {
                DrawCenterToCenterLinkedLine(ref startRect, ref endRect);
            }
            else
            {
                if (this.startLinkTo == LinkToPoint.Center)
                {
                    //先求EndPoint
                    Point? endPoint = GetLinkedPoint(endRect, this.endLinkTo);
                    if (endPoint != null && endPoint.HasValue)
                    {
                        PointToRect.ArrowPoints aptStart = PointToRect.GetCrossPointToRect(startRect, endPoint.Value);
                        startArrowPathFigure.StartPoint = aptStart.Top;

                        if (startArrowPath.Visibility == Visibility.Visible)
                        {
                            Tools.ArrowPoints apStart = new Tools.ArrowPoints(
                                aptStart.Top, endPoint.Value, widgetLineWidth * 2);

                            startArrowPolyLineSegment.Points = new PointCollection(){
                                apStart.ArrowLeftPoint,apStart.ArrowRightPoint,
                            };

                            mainLine.X1 = apStart.ArrowRealTopPoint.X;
                            mainLine.Y1 = apStart.ArrowRealTopPoint.Y;
                        }
                        else
                        {
                            mainLine.X1 = aptStart.Top.X;
                            mainLine.Y1 = aptStart.Top.Y;
                        }

                        endArrowPathFigure.StartPoint = endPoint.Value;
                        if (endArrowPath.Visibility == Visibility.Visible)
                        {
                            Tools.ArrowPoints apEnd = new Tools.ArrowPoints(
                            endPoint.Value, aptStart.Top, widgetLineWidth * 2);
                            endArrowPolyLineSegment.Points = new PointCollection(){
                                apEnd.ArrowLeftPoint,apEnd.ArrowRightPoint,
                            };

                            mainLine.X2 = apEnd.ArrowRealTopPoint.X;
                            mainLine.Y2 = apEnd.ArrowRealTopPoint.Y;
                        }
                        else
                        {
                            mainLine.X2 = endPoint.Value.X;
                            mainLine.Y2 = endPoint.Value.Y;
                        }

                    }
                }
                else if (this.endLinkTo == LinkToPoint.Center)
                {
                    //先求StartPoint
                    Point? startPoint = GetLinkedPoint(startRect, this.startLinkTo);
                    if (startPoint != null && startPoint.HasValue)
                    {
                        PointToRect.ArrowPoints aptEnd = PointToRect.GetCrossPointToRect(endRect, startPoint.Value);
                        endArrowPathFigure.StartPoint = aptEnd.Top;
                        if (endArrowPath.Visibility == Visibility.Visible)
                        {
                            Tools.ArrowPoints apEnd = new Tools.ArrowPoints(
                            aptEnd.Top, startPoint.Value, widgetLineWidth * 2);
                            endArrowPolyLineSegment.Points = new PointCollection(){
                                apEnd.ArrowLeftPoint,apEnd.ArrowRightPoint,
                            };

                            mainLine.X2 = apEnd.ArrowRealTopPoint.X;
                            mainLine.Y2 = apEnd.ArrowRealTopPoint.Y;
                        }
                        else
                        {
                            mainLine.X2 = aptEnd.Top.X;
                            mainLine.Y2 = aptEnd.Top.Y;
                        }

                        startArrowPathFigure.StartPoint = startPoint.Value;

                        if (startArrowPath.Visibility == Visibility.Visible)
                        {
                            Tools.ArrowPoints apStart = new Tools.ArrowPoints(
                                startPoint.Value, aptEnd.Top, widgetLineWidth * 2);

                            startArrowPolyLineSegment.Points = new PointCollection(){
                                apStart.ArrowLeftPoint,apStart.ArrowRightPoint,
                            };

                            mainLine.X1 = apStart.ArrowRealTopPoint.X;
                            mainLine.Y1 = apStart.ArrowRealTopPoint.Y;
                        }
                        else
                        {
                            mainLine.X1 = startPoint.Value.X;
                            mainLine.Y1 = startPoint.Value.Y;
                        }
                    }
                }
                else
                {
                    //两端都不指向中心
                    Point? startPoint = GetLinkedPoint(startRect, this.startLinkTo);
                    Point? endPoint = GetLinkedPoint(endRect, this.endLinkTo);
                    if (startPoint != null && startPoint.HasValue && endPoint != null && endPoint.HasValue)
                    {
                        startArrowPathFigure.StartPoint = startPoint.Value;

                        if (startArrowPath.Visibility == Visibility.Visible)
                        {
                            Tools.ArrowPoints apStart = new Tools.ArrowPoints(
                                startPoint.Value, endPoint.Value, widgetLineWidth * 2);

                            startArrowPolyLineSegment.Points = new PointCollection(){
                                apStart.ArrowLeftPoint,apStart.ArrowRightPoint,
                            };

                            mainLine.X1 = apStart.ArrowRealTopPoint.X;
                            mainLine.Y1 = apStart.ArrowRealTopPoint.Y;
                        }
                        else
                        {
                            mainLine.X1 = startPoint.Value.X;
                            mainLine.Y1 = startPoint.Value.Y;
                        }

                        endArrowPathFigure.StartPoint = endPoint.Value;
                        if (endArrowPath.Visibility == Visibility.Visible)
                        {
                            Tools.ArrowPoints apEnd = new Tools.ArrowPoints(
                            endPoint.Value, startPoint.Value, widgetLineWidth * 2);
                            endArrowPolyLineSegment.Points = new PointCollection(){
                                apEnd.ArrowLeftPoint,apEnd.ArrowRightPoint,
                            };

                            mainLine.X2 = apEnd.ArrowRealTopPoint.X;
                            mainLine.Y2 = apEnd.ArrowRealTopPoint.Y;
                        }
                        else
                        {
                            mainLine.X2 = endPoint.Value.X;
                            mainLine.Y2 = endPoint.Value.Y;
                        }

                    }
                }
            }
        }

        /// <summary>
        /// 当linkToPoint的值为Center时，返回null。
        /// </summary>
        internal Point? GetLinkedPoint(Rect linkedRect, LinkToPoint linkToPoint)
        {
            switch (linkToPoint)
            {
                case LinkToPoint.TopLeft: return linkedRect.TopLeft;
                case LinkToPoint.Top: return new Point(linkedRect.Left + linkedRect.Width / 2, linkedRect.Top);
                case LinkToPoint.TopRight: return linkedRect.TopRight;
                case LinkToPoint.Left: return new Point(linkedRect.Left, linkedRect.Top + linkedRect.Height / 2);
                case LinkToPoint.Right: return new Point(linkedRect.Right, linkedRect.Top + linkedRect.Height / 2);
                case LinkToPoint.BottomLeft: return linkedRect.BottomLeft;
                case LinkToPoint.Bottom: return new Point(linkedRect.Left + linkedRect.Width / 2, linkedRect.Bottom);
                case LinkToPoint.BottomRight: return linkedRect.BottomRight;
                default: return null;
            }
        }

        private void DrawCenterToCenterLinkedLine(ref Rect startRect, ref Rect endRect)
        {
            Point startCenter = new Point(startRect.X + startRect.Width / 2, startRect.Y + startRect.Height / 2);
            Point endCenter = new Point(endRect.X + endRect.Width / 2, endRect.Y + endRect.Height / 2);

            PointToRect.ArrowPoints aptStart = PointToRect.GetCrossPointToRect(startRect, endCenter);
            startArrowPathFigure.StartPoint = aptStart.Top;

            if (startArrowPath.Visibility == Visibility.Visible)
            {
                Tools.ArrowPoints apStart = new Tools.ArrowPoints(
                    aptStart.Top, endCenter, widgetLineWidth * 2);

                startArrowPolyLineSegment.Points = new PointCollection(){
                    apStart.ArrowLeftPoint,apStart.ArrowRightPoint,
                };

                mainLine.X1 = apStart.ArrowRealTopPoint.X;
                mainLine.Y1 = apStart.ArrowRealTopPoint.Y;
            }
            else
            {
                mainLine.X1 = aptStart.Top.X;
                mainLine.Y1 = aptStart.Top.Y;
            }

            PointToRect.ArrowPoints aptEnd = PointToRect.GetCrossPointToRect(endRect, startCenter);
            endArrowPathFigure.StartPoint = aptEnd.Top;
            if (endArrowPath.Visibility == Visibility.Visible)
            {
                Tools.ArrowPoints apEnd = new Tools.ArrowPoints(
                aptEnd.Top, startCenter, widgetLineWidth * 2);
                endArrowPolyLineSegment.Points = new PointCollection(){
                    apEnd.ArrowLeftPoint,apEnd.ArrowRightPoint,
                };

                mainLine.X2 = apEnd.ArrowRealTopPoint.X;
                mainLine.Y2 = apEnd.ArrowRealTopPoint.Y;
            }
            else
            {
                mainLine.X2 = aptEnd.Top.X;
                mainLine.Y2 = aptEnd.Top.Y;
            }

        }

        /// <summary>
        /// 重定文本位置。
        /// </summary>
        /// <param name="startPoint">首端点。</param>
        /// <param name="endPoint">尾端点。</param>
        private void LocateTextPanel(Point startPoint, Point endPoint)
        {
            Point center = new Point(startPoint.X + (endPoint.X - startPoint.X) / 2,
                            startPoint.Y + (endPoint.Y - startPoint.Y) / 2);

            Point textStart = new Point(center.X - this.mainBorder.ActualWidth / 2 - this.mainBorder.Margin.Left,
                center.Y - this.mainBorder.ActualHeight / 2 - this.mainBorder.Margin.Top);

            Canvas.SetLeft(this.mainBorder, textStart.X);
            Canvas.SetTop(this.mainBorder, textStart.Y);
        }

        private void LocateTextPanel(Rect startRect, Rect endRect)
        {
            //边缘留点空白区。
            startRect.X -= 4; startRect.Y -= 4; startRect.Width += 8; startRect.Height += 8;
            endRect.X -= 4; endRect.Y -= 4; endRect.Width += 8; endRect.Height += 8;

            Point startCenter = new Point(startRect.Left + startRect.Width / 2,
                startRect.Top + startRect.Height / 2);
            Point endCenter = new Point(endRect.Left + endRect.Width / 2,
                endRect.Top + endRect.Height / 2);

            Point startPoint, endPoint;
            if (this.startLinkTo == LinkToPoint.Center && this.endLinkTo == LinkToPoint.Center)
            {
                Tools.PointToRect.ArrowPoints aptStart = Tools.PointToRect.GetCrossPointToRect(startRect, endCenter);
                Tools.PointToRect.ArrowPoints aptEnd = Tools.PointToRect.GetCrossPointToRect(endRect, startCenter);

                startPoint = aptStart.Top;
                endPoint = aptEnd.Top;
            }
            else
            {
                if (this.startLinkTo == LinkToPoint.Center)
                {
                    Point? newEndPoint = this.GetLinkedPoint(endRect, this.endLinkTo);
                    if (newEndPoint != null && newEndPoint.HasValue)
                    {
                        endPoint = newEndPoint.Value;
                        Tools.PointToRect.ArrowPoints aptStart = Tools.PointToRect.GetCrossPointToRect(startRect, endPoint);
                        startPoint = aptStart.Top;
                    }
                    else
                    {
                        Tools.PointToRect.ArrowPoints aptStart = Tools.PointToRect.GetCrossPointToRect(startRect, endCenter);
                        Tools.PointToRect.ArrowPoints aptEnd = Tools.PointToRect.GetCrossPointToRect(endRect, startCenter);

                        startPoint = aptStart.Top;
                        endPoint = aptEnd.Top;
                    }
                }
                else if (this.endLinkTo == LinkToPoint.Center)
                {
                    Point? newStartPoint = this.GetLinkedPoint(startRect, this.startLinkTo);
                    if (newStartPoint != null && newStartPoint.HasValue)
                    {
                        startPoint = newStartPoint.Value;
                        Tools.PointToRect.ArrowPoints aptEnd = Tools.PointToRect.GetCrossPointToRect(endRect, startPoint);
                        endPoint = aptEnd.Top;
                    }
                    else
                    {
                        Tools.PointToRect.ArrowPoints aptStart = Tools.PointToRect.GetCrossPointToRect(startRect, endCenter);
                        Tools.PointToRect.ArrowPoints aptEnd = Tools.PointToRect.GetCrossPointToRect(endRect, startCenter);

                        startPoint = aptStart.Top;
                        endPoint = aptEnd.Top;
                    }
                }
                else
                {
                    Point? newStartPoint = this.GetLinkedPoint(startRect, this.startLinkTo);
                    Point? newEndPoint = this.GetLinkedPoint(endRect, this.endLinkTo);

                    if (newStartPoint != null && newStartPoint.HasValue && newEndPoint != null && newEndPoint.HasValue)
                    {
                        startPoint = newStartPoint.Value;
                        endPoint = newEndPoint.Value;
                    }
                    else
                    {
                        Tools.PointToRect.ArrowPoints aptStart = Tools.PointToRect.GetCrossPointToRect(startRect, endCenter);
                        Tools.PointToRect.ArrowPoints aptEnd = Tools.PointToRect.GetCrossPointToRect(endRect, startCenter);

                        startPoint = aptStart.Top;
                        endPoint = aptEnd.Top;
                    }
                }
            }

            LocateTextPanel(startPoint, endPoint);//都不是成员字段。
        }

        private Point FormatStartMovingPoint(Point newInsideMovingPoint, Point endPoint)
        {
            double horizontalDistance = newInsideMovingPoint.X - endPoint.X;
            double verticalDistance = newInsideMovingPoint.Y - endPoint.Y;

            double absHD = Math.Abs(horizontalDistance);
            double absVD = Math.Abs(verticalDistance);

            if (absVD > absHD)
            {
                if (absVD > absHD * 2)
                {
                    newInsideMovingPoint = new Point(endPoint.X, newInsideMovingPoint.Y);
                }
                else
                {
                    int qua = Getquadrant(newInsideMovingPoint, endPoint);
                    switch (qua)
                    {
                        case 1:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 2:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 3:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 4:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                    endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                    }
                }
            }
            else
            {
                if (absVD * 2 < absHD)
                {
                    newInsideMovingPoint = new Point(newInsideMovingPoint.X, endPoint.Y);
                }
                else
                {
                    int qua = Getquadrant(newInsideMovingPoint, endPoint);
                    switch (qua)
                    {
                        case 1:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 2:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 3:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                   endPoint.Y - (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                        case 4:
                            {
                                newInsideMovingPoint = new Point(newInsideMovingPoint.X,
                                    endPoint.Y + (newInsideMovingPoint.X - endPoint.X));
                                break;
                            }
                    }
                }
            }

            return newInsideMovingPoint;
        }

        /// <summary>
        /// 以basePoint为基础坐标，画出四象限。
        /// 取出pt在哪个象限中。
        /// </summary>
        /// <param name="pt"></param>
        /// <param name="basePoint"></param>
        /// <returns></returns>
        private int Getquadrant(Point pt, Point basePoint)
        {
            if (pt.X > basePoint.X)
            {
                if (pt.Y > basePoint.Y)
                {
                    return 4;
                }
                else
                {
                    return 1;
                }
            }
            else
            {
                if (pt.Y > basePoint.Y)
                {
                    return 3;
                }
                else
                {
                    return 2;
                }
            }
        }

        public override string GetRelativeOuterXml(Point baseCopyTopLeft)
        {
            if (this.xmlData == null) return string.Empty;

            Point oldStartPoint = startPoint;
            Point newStartPoint = new Point(oldStartPoint.X - baseCopyTopLeft.X,
                oldStartPoint.Y - baseCopyTopLeft.Y);
            this.xmlData.SetAttribute(XmlTags.StartPointTag, newStartPoint.ToString());

            Point oldEndPoint = endPoint;
            Point newEndPoint = new Point(oldEndPoint.X - baseCopyTopLeft.X,
                oldEndPoint.Y - baseCopyTopLeft.Y);
            this.xmlData.SetAttribute(XmlTags.EndPointTag, newEndPoint.ToString());

            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            sb.Append(this.xmlData.OuterXml);

            this.xmlData.SetAttribute(XmlTags.StartPointTag, oldStartPoint.ToString());
            this.xmlData.SetAttribute(XmlTags.EndPointTag, oldEndPoint.ToString());

            return sb.ToString();
        }

        /// <summary>
        /// 这个虚方法是用以查看本部件是否在选定框的内部。
        /// 线型部件，各有各的计算办法。
        /// </summary>
        public override bool IsInRect(Rect rect)
        {
            //return base.IsInRect(rect);//这个要屏蔽。
            bool isInRect = base.IsInRect(rect);
            if (isInRect)
            {
                return true;
            }
            else
            {
                //这个法子还不完善。线是否“穿过”矩形还得另想办法。
                return rect.Contains(startPoint) || rect.Contains(endPoint);
            }
        }

        /// <summary>
        /// 刷新线宽。
        /// </summary>
        public override void RefreshWidgetLineWidth()
        {
            base.RefreshWidgetLineWidth();

            //强制直线跳过宽度１（当作２看待）——因为会模糊，很难看。
            //mainLine.StrokeThickness = (widgetLineWidth < 2 ? 2 : widgetLineWidth);

            double newLineWidth = (widgetLineWidth < 1 ? 1 : widgetLineWidth);
            mainLine.StrokeThickness = newLineWidth;//还是支持宽度1的线为妙。

            if (widgetLineWidth == 1)
            {
                if ((int)this.mainLine.X1 == (int)this.mainLine.X2 || (int)this.mainLine.Y1 == (int)this.mainLine.Y2)
                {
                    RenderOptions.SetEdgeMode(mainLine, EdgeMode.Aliased);
                }
                else
                {
                    RenderOptions.SetEdgeMode(mainLine, EdgeMode.Unspecified);
                }
            }
            else
            {
                RenderOptions.SetEdgeMode(mainLine, EdgeMode.Unspecified);
            }

            startArrowPath.StrokeThickness = endArrowPath.StrokeThickness = newLineWidth;
        }

        public override void RefreshLocation()
        {
            base.RefreshLocation();

            DrawLine();

            RefreshStartPoint();
            RefreshEndPoint();

            //RefreshArrows();//this.RefreshWidgetLineWidth()方法中会调用。

            this.RefreshTextRotateAngle();
            this.RefreshWidgetLineWidth();

            //if (IsLinked)
            //{
            //    if (Math.Abs(startPoint.X - endPoint.X) <= 2 && Math.Abs(startPoint.Y - endPoint.Y) <= 2)
            //    {
            //        startArrowPath.Visibility = endArrowPath.Visibility =
            //            mainLine.Visibility = Visibility.Hidden;
            //    }
            //    else
            //    {
            //        mainLine.Visibility = Visibility.Visible;
            //    }
            //}
        }

        public override void RefreshLineDash()
        {
            switch (lineDash)
            {
                case LineDashType.DashType.Dash:
                    {
                        this.mainLine.StrokeDashArray = LineDashType.dashCollection; break;
                    }
                case LineDashType.DashType.DashDotDot:
                    {
                        this.mainLine.StrokeDashArray = LineDashType.dashDotDotCollection; break;
                    }
                case LineDashType.DashType.Dot:
                    {
                        this.mainLine.StrokeDashArray = LineDashType.dotCollection; break;
                    }
                case LineDashType.DashType.DashDot:
                    {
                        this.mainLine.StrokeDashArray = LineDashType.dashDotCollection; break;
                    }
                default:
                    {
                        this.mainLine.StrokeDashArray = LineDashType.solidCollection; break;
                    }
            }
        }

        private void RefreshEndPoint()
        {
            mainLine.X2 = endPoint.X;
            mainLine.Y2 = endPoint.Y;
        }

        public override void RefreshIsShadowVisible()
        {
            base.RefreshIsShadowVisible();

            if (isShadowVisible)
            {
                this.mainLine.Effect = Widget.ShadowEffect;
            }
            else
            {
                this.mainLine.Effect = null;
            }
        }

        private void RefreshStartPoint()
        {
            mainLine.X1 = startPoint.X;
            mainLine.Y1 = startPoint.Y;
        }

        public override void RefreshPointWhenGroupIn(Point baseTopLeft)
        {
            Point oldStartPoint = startPoint;
            Point newStartPoint = new Point(oldStartPoint.X - baseTopLeft.X,
                oldStartPoint.Y - baseTopLeft.Y);
            StartPoint = newStartPoint;

            Point oldEndPoint = endPoint;
            Point newEndPoint = new Point(oldEndPoint.X - baseTopLeft.X,
                oldEndPoint.Y - baseTopLeft.Y);
            EndPoint = newEndPoint;
        }

        public override void RefreshPointWhenGroupOut(Point baseTopLeft)
        {
            Point oldStartPoint = startPoint;
            Point newStartPoint = new Point(oldStartPoint.X + baseTopLeft.X,
                oldStartPoint.Y + baseTopLeft.Y);
            StartPoint = newStartPoint;

            Point oldEndPoint = endPoint;
            Point newEndPoint = new Point(oldEndPoint.X + baseTopLeft.X,
                oldEndPoint.Y + baseTopLeft.Y);
            EndPoint = newEndPoint;
        }

        public override void RefreshTextPanelLocatin()
        {
            LocateTextPanel(this.startPoint, this.endPoint);
        }

        /// <summary>
        /// 刷新文本区旋转角度。
        /// </summary>
        public void RefreshTextRotateAngle()
        {
            if (this.mainBorder.RenderTransformOrigin != DefaultRenderCenter)
            {
                this.mainBorder.RenderTransformOrigin = DefaultRenderCenter;
            }

            if (this.textRotateAngle == 0)
            {
                this.mainBorder.RenderTransform = DefaultRotateTransform;
            }
            else
            {
                this.mainBorder.RenderTransform = new RotateTransform(textRotateAngle);
            }
        }

        public override void RefreshWidgetLineColor()
        {
            startArrowPath.Stroke = endArrowPath.Stroke =
                startArrowPath.Fill = endArrowPath.Fill =
                mainLine.Fill = mainLine.Stroke = this.widgetLineColor;
        }

        #endregion


        #region 事件=========================================================================================================


        #endregion

    }
}
